<template>
    <div>
        <h3>响应式 API : 工具函数</h3>
    </div>
</template>

<script lang="ts">
export default {
    name: 'diyDemo35'
}
</script>


<script setup lang='ts'>
import { isProxy, isRef, reactive, readonly, ref, toRef, toRefs, unref, type Ref } from 'vue';

let num = 0
let dataNum: Ref<number> = ref(0)

//  ref
let dataObj1 = ref({
    count: 0,
    strArr: ['php', '刘强东', '马云', '马化腾', '任正非'],
    user: {
        name: '许家印',
        sex: 'nan',
        age: 42
    }
})

// reactive
let dataObj2 = reactive({
    count: 0,
    strArr: ['php', '刘强东', '马云', '马化腾', '任正非'],
    user: {
        name: '许家印',
        sex: 'nan',
        age: 42,
        aihao: {}
    }
})

// 普通对象
let dataObj3 = {
    count: 0,
    strArr: ['php', '刘强东', '马云', '马化腾', '任正非'],
    user: {
        name: '许家印',
        sex: 'nan',
        age: 42,
        aihao: {}
    }
}

// isRef() 检查某个值是否为 ref , 返回布尔值
console.log(isRef(num)); // false
console.log(isRef(dataObj1)); // true
console.log(isRef(dataNum)); // true
console.log(isRef(dataObj2)); // false
console.log(isRef(dataObj3)); // false

// unref() 卸载ref包装
// 如果参数是ref包装的，则返回内部值，否则返回本身。这是 val = isRef(val) ? val.value : val 计算的一个语法糖。

console.log(dataNum);
console.log(unref(dataNum));  // 0

function myUnref(data: any) {
    return isRef(data) ? data.value : data
}
console.log(myUnref(dataNum)); // 0



// toRef() 可以将值、refs 或 getters 规范化 为 refs (3.3+)
// ref初始值的时候用 toRef()

console.log(toRef(num)); // 将普通变量转成ref包装的变量
console.log(toRef(dataObj3)); // 将普通对象转成 ref包装的对象

let newDataObj3: any = {}
for (const key in dataObj3) {
    if (Object.prototype.hasOwnProperty.call(dataObj3, key)) {
        newDataObj3[key as keyof typeof dataObj3] = toRef(newDataObj3[key as keyof typeof dataObj3]);

    }
}
console.log(newDataObj3);
console.log(newDataObj3.count);
console.log(newDataObj3.strArr);
console.log(newDataObj3.user);


// 创建一个只读的 ref ，当访问 .value 时 会调用此getter 函数
let newRefCount = toRef(() => {
    return dataObj3.count
})
console.log(newRefCount.value); // 0

// 按原样返回现有的ref
let dataObj12 = toRef(dataObj1)
console.log(dataObj12.value === dataObj1.value); // true

// 也可以基于响应式对象上的一个属性，创建一个对应的ref。这样创建的ref与其源属性保持同步：改变原属性的值将更新 ref 的值，反之亦然。

const state = reactive({
    foo: 1,
    bar: 2
})

// 双向 ref，会与原属性同步
const fooRef = toRef(state, 'foo')
// fooRef.value = 300
// console.log(state.foo)
state.foo = 10000
console.log(fooRef.value)

const stateAsRefs = toRefs(state)

stateAsRefs.foo.value = 666
console.log(stateAsRefs.foo.value); // 666
console.log(stateAsRefs.bar.value); // 2

const { foo, bar } = stateAsRefs

console.log(foo.value, bar.value); // 666 2

console.log(stateAsRefs.foo) // 666

// readonly()
// 接受一个对象（不论是响应式还是普通的）或是一个 ref，返回一个原值的只读代理。

let rlDataNum = readonly(dataNum)
console.log(rlDataNum.value); // 0
// rlDataNum.value = 333 // 无法为“value”赋值，因为它是只读属性。

let rldataObj1 = readonly(dataObj1)
console.log(rldataObj1.value.count); // 0
// rldataObj1.value.count = 2 // 无法为“count”赋值，因为它是只读属性

let rldataObj2 = readonly(dataObj2)
console.log(rldataObj2.count); // 0
// rldataObj2.count = 5 // 无法为“count”赋值，因为它是只读属性。
// rldataObj2.strArr.push() // 类型“readonly string[]”上不存在属性“push”。

let rldataObj3 = readonly(dataObj3)
console.log(rldataObj3.user.age); // 42
// rldataObj3.count = 5 // 无法为“count”赋值，因为它是只读属性。
// rldataObj3.user.age = 45 // 无法为“age”赋值，因为它是只读属性。



// isProxy()
// 检查一个对象是否是由 reactive() , readonly() , shallowReactive(), 或 shallowReadonly() 创建的代理，是返回true，否则返回false

console.log(isProxy(dataObj2)); // true
console.log(isProxy(dataObj1)); // false




</script>

<style></style>